import base64
import json
import requests
import urllib
import sys
import time

from requests.structures import CaseInsensitiveDict
from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BUY = "BUY"
  SELL = "SELL"

class Type(Enum):
  LIMIT = "limit"
  STOP_LIMIT = "stop_limit"
  TAKE_LIMIT = "take_limit"
  MARKET = "market"
  STOP_MARKET = "stop_market"
  TAKE_MARKET = "take_market"
  MARKET_LIMIT = "market_limit"
  TRAILING_STOP = "trailing_stop"

class DeribitAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_authentication(self, path: str):
    if "private" in path:
      # Authorization: Basic BASE64(ClientId + : + ClientSecret)
      before_base64 = self._key.access_key + ":" + self._key.secret_key
      base64ed = base64.b64encode(before_base64.encode())
      
      return base64ed.decode()

  def __call__(self, req: requests.PreparedRequest):
    path = urllib.parse.urlparse(req.url).path

    authentication = self._gen_authentication(path)
    req.headers.update({'Authorization': "Basic {}".format(authentication)})
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class DeribitPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = DeribitAuth(key_path)
    self._url = "https://www.deribit.com"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_asset(self, currency: str):
    assert currency, "currency must be not None and not empty!"

    method = "GET"
    params = {
        'currency': currency,
    }
    path = "/api/v2/private/get_account_summary"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_portfolio_asset(self):
    method = "GET"
    params = {
        'with_portfolio': 'true',
    }
    path = "/api/v2/private/get_subaccounts"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_open_order(self, currency: str, kind: str):
    assert currency, "currency must be not None and not empty!"

    method = "GET"
    params = {
        'currency': currency,
        'kind': kind,
        'type': 'all',
    }
    path = "/api/v2/private/get_open_orders_by_currency"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_hist_order(self, currency: str, kind: str):
    assert currency, "currency must be not None and not empty!"

    method = "GET"
    params = {
        'currency': currency,
        'kind': kind,
        'count': 50,
        'include_unfilled': 'true',
    }
    path = "/api/v2/private/get_order_history_by_currency"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_fill_order(self, currency: str, kind: str):
    assert currency, "currency must be not None and not empty!"

    method = "GET"
    params = {
        'currency': currency,
        'kind': kind,
        'count': 50,
    }
    path = "/api/v2/private/get_user_trades_by_currency"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def cancel_order(self, order_id: str):
    assert order_id, "order_id must be not None and not empty!"

    method = "GET"
    params = {'order_id': order_id}
    path = "/api/v2/private/cancel"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def submit_order(
    self,
    instrument_name: str,
    side: Side,
    amount: str,
    type: Type = None,
    price: str = None,
    advanced: str = None
  ):
    assert instrument_name, "instrument_name must be not None and not empty!"
    assert side, "side must be not None!"
    assert amount, "amount must be not None and not empty!"

    method = "GET"
    params = {
      'instrument_name': instrument_name,
      'amount': amount,
      'type': type and type.value,
      'price': price,
      #'post_only': 'true',
      #'reject_post_only': "true",
      'label': str(int(time.time() * 1000)),
      #'advanced': 'implv',
    }

    path = None
    if side == Side.BUY:
      path = "/api/v2/private/buy"
    elif side == Side.SELL:
      path = "/api/v2/private/sell"
    else:
      assert False, "side must be one of buy and sell!"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_deposits(self, currency, count=1000):
    method = "GET"

    params = {
      "currency":currency,
      "count": count
    }

    path = "/api/v2/private/get_deposits"
    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_withdraw(self, currency, count=1000):
    method = "GET"

    params = {
      "currency":currency,
      "count":count
    }

    path = "/api/v2/private/get_withdrawals"
    response = self._query.query(method=method, path=path, params=params)
    return response.json()


  def get_transfer(self, currency, count=1000000):
    method = "GET"

    params = {
      "currency":currency,
      "count":count
    }

    path = "/api/v2/private/get_transfers"
    response = self._query.query(method=method, path=path, params=params)
    return response.json()

# api doc url
# https://docs.deribit.com/

if __name__ == "__main__":
  key_path = sys.argv[1]

  deribit = DeribitPrivateClient(key_path)
  #info = deribit.get_open_order("ETH", 'future')
  #info = deribit.get_hist_order("ETH", 'future')
  #info = deribit.get_fill_order("ETH", 'future')
  #info = deribit.cancel_order("123456677")
  #info = deribit.submit_order("ETH-5MAY23-1650-P", Side.SELL, "1", Type.LIMIT, "60")
  #info = deribit.submit_order("ETH-5MAY23-1650-P", Side.BUY, "1", Type.LIMIT, "75")
  #info = deribit.submit_order("ETH-PERPETUAL", Side.SELL, "1", Type.LIMIT, "1830")
  #info = deribit.submit_order("ETH-PERPETUAL", Side.BUY, "1", Type.LIMIT, "1850")
  #info = deribit.cancel_order("55484611956")
  #print(json.dumps(info, indent=2))

  #info = deribit.get_portfolio_asset()
  #info = deribit.get_deposits("USDC")
  #info = deribit.get_withdraw("USDC")
  info = deribit.get_transfer("USDC")

  print(json.dumps(info, indent=2))

